/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
==================================================================== */
// copy code from POI CellReference
package com.gitee.wsl.struct.table.utils

import com.gitee.wsl.ext.base.ifValue
import com.gitee.wsl.struct.table.CellPosition
import com.gitee.wsl.transform.validator.string.StringValidator
import com.gitee.wsl.transform.validator.string.ext.allDigit
import com.gitee.wsl.transform.validator.validateUse


object CellReferenceHelper {
    const val ABSOLUTE_REFERENCE_MARKER: Char = '$'

    /**
     * Takes in a 0-based base-10 column and returns a ALPHA-26 representation. eg `convertNumToColString(3)`
     * returns `"D"`
     */
    fun convertNumToColString(col: Int): String {
        // Excel counts column A as the 1st column, we
        // treat it as the 0th one
        val excelColNum = col + 1

        val colRef = StringBuilder(2)
        var colRemain = excelColNum

        while (colRemain > 0) {
            var thisPart = colRemain % 26
            if (thisPart == 0) {
                thisPart = 26
            }
            colRemain = (colRemain - thisPart) / 26

            // The letter A is at 65
            val colChar = (thisPart + 64).toChar()
            colRef.insert(0, colChar)
        }

        return colRef.toString()
    }

    fun isPartAbsolute(part: String): Boolean {
        return part[0] == ABSOLUTE_REFERENCE_MARKER
    }

    /**
     * takes in a column reference portion of a CellRef and converts it from ALPHA-26 number format to 0-based base 10.
     * 'A' -&gt; 0 'Z' -&gt; 25 'AA' -&gt; 26 'IV' -&gt; 255
     *
     * @return zero based column index
     */
    fun convertColStringToIndex(ref: String): Int {
        if (ref.isBlank()) return -1

        var retval = 0
        val refs = ref.uppercase().toCharArray()
        for (k in refs.indices) {
            val thechar = refs[k]
            if (thechar == ABSOLUTE_REFERENCE_MARKER) {
                if (k != 0) {
                    throw IllegalArgumentException("Bad col ref format '$ref'")
                }
                continue
            }

            // Character is uppercase letter, find relative value to A
            retval = (retval * 26) + (thechar.code - 'A'.code + 1)
        }
        return retval - 1
    }

    fun parsePositionABString(abStr: String): CellPosition? {
        var abStr = abStr
        if (abStr.isBlank()) return null

        if (CellPosition.NONE_NAME == abStr) return CellPosition.NONE

        val str = abStr

        val pos = abStr.indexOf(ABSOLUTE_REFERENCE_MARKER)
        if (pos > 0) abStr = abStr.substring(pos + 1)

        var p = 0
        var i = 0
        val n = abStr.length
        while (i < n) {
            val c = abStr[i]
            if (c in '0'..'9') break
            i++
            p++
        }
        val colStr = abStr.substring(0, p)
        val rowStr = abStr.substring(p)

        val colIndex = convertColStringToIndex(colStr)
        val rowIndex = rowStr.isEmpty().ifValue(-1,rowStr.toInt() - 1)
        /*val rowIndex = if (rowStr.isEmpty()) -1
        else rowStr.toInt() - 1 //{ err -> NopException(err).param(ARG_CELL_POS, str) } - 1*/
        return CellPosition.of(rowIndex, colIndex)
    }

    /**
     * 要求必须是 大写字母 + 数字这种组合形式，不允许使用小写字母
     *
     * @param abStr Excel单元格的位置表达式，例如A3
     */
    fun isABString(abStr: String): Boolean {
        if (abStr.isBlank()) return false

        var p = 0
        var i = 0
        val n = abStr.length
        while (i < n) {
            val c = abStr[i]
            if (c in '0'..'9') break
            i++
            p++
        }

        val colStr = abStr.substring(0, p)
        val rowStr = abStr.substring(p)

        if (!isColStr(colStr)) return false

        if (rowStr.isEmpty() || rowStr.length > 8) return false

        return rowStr.validateUse(StringValidator.allDigit())
    }

    private fun isColStr(str: String): Boolean {
        if (str.isEmpty() || str.length > 5) return false

        var i = 0
        val n = str.length
        while (i < n) {
            val c = str[0]
            if (c > 'Z' || c < 'A') return false
            i++
        }
        return true
    }

    fun parsePositionRCString(str: String): CellPosition? {
        return null
    }
}